#define _GNU_SOURCE 1

#include <fcntl.h>
#include <iostream>
#include <arpa/inet.h>
#include <cassert>
#include <sys/poll.h>
#include <cstring>
#include <sys/unistd.h>

inline constexpr int getBufferSize() {
    return 64;
}

struct ClientData {
    sockaddr_in sockAddrIn;
    char* writeBuffer;
    char buffer[getBufferSize()];
};

int setNonBlocking(int fd) {
    int oldOption = fcntl(fd, F_GETFL);
    int newOption = oldOption | O_NONBLOCK;
    fcntl(fd, F_SETFL, newOption);
    return oldOption;
}


/**
 * @brief
 *
 */
int main(int argc, char** argv) {
    if (argc <= 2) {
        std::cout << "usage: " << basename(argv[0]) << " IPAddress PortNumber" << std::endl;
        return 1;
    }
    const char* ip = argv[1];
    int port = std::atoi(argv[2]);

    sockaddr_in sockAddrIn{
            .sin_family=PF_INET,
            .sin_port=htons(port),
            {.s_addr=0}
    };
    inet_pton(AF_INET, ip, &sockAddrIn.sin_addr);

    int listenFD = socket(PF_INET, SOCK_STREAM, 0);
    assert(listenFD >= 0);

    int ret = bind(listenFD, (sockaddr * ) & sockAddrIn, sizeof(sockAddrIn));
    assert(ret != -1);

    ret = listen(listenFD, 5);
    assert(ret != -1);

    /*
     * 创建users数组, 分配FD_LIMIT个ClientData对象。
     * 可以预测: 每个可能的socket连接都可以获得一个这样的对象, 并且socket的值可以直接用来索引(作为数组下标)socket连接对应的ClientData对象,
     * 这是将socket和客户数据关联的简单而高效的方式。
     */
    int FD_LIMIT = 65535;
    ClientData* clientData = new ClientData[FD_LIMIT];
    // 尽管我们分配了足够多的ClientData对象, 但为了提高poll的性能, 仍然有必要限制用户的数量。
    int USER_LIMIT = 5;
    pollfd fds[USER_LIMIT + 1];
    for (int i = 1; i < USER_LIMIT; ++i) {
        fds[i].fd = -1;
        fds[i].events = 0;
    }
    fds[0].fd = listenFD;
    fds[0].events = POLLIN | POLLERR;
    fds[0].revents = 0;
    int userCounter = 0;
    for (;;) {
        ret = poll(fds, userCounter + 1, -1);
        if (ret < 0) {
            std::printf("poll failure\n");
            break;
        }
        for (int i = 0; i < userCounter + 1; ++i) {
            if ((fds[i].fd == listenFD) && (fds[i].revents & POLLIN)) {
                sockaddr_in clientSockAddrIn;
                socklen_t clientSockAddrInLength = sizeof(clientSockAddrIn);
                int connectFD = accept(listenFD, (sockaddr * ) & clientSockAddrIn, &clientSockAddrInLength);
                if (connectFD < 0) {
                    std::printf("errno is: %d\n", errno);
                    continue;
                }
                // 如果请求太多, 则关闭新到的连接
                if (userCounter >= USER_LIMIT) {
                    const char* info = "too many users\n";
                    std::printf("%s", info);
                    send(connectFD, info, std::strlen(info), 0);
                    close(connectFD);
                    continue;
                }
                /*
                 * 对于新的连接, 同时修改fds和clientData数组。前文已经提到, clientData[connectFD]对应于
                 * 新连接文件描述符connectFD的客户数据
                 */
                ++userCounter;
                clientData[connectFD].sockAddrIn = clientSockAddrIn;
                setNonBlocking(connectFD);
                fds[userCounter].fd = connectFD;
                fds[userCounter].events = POLLIN | POLLRDHUP | POLLERR;
                fds[userCounter].revents = 0;
                std::printf("comes a new user, now have %d users\n", userCounter);
            } else if (fds[i].revents & POLLERR) {
                std::printf("get an error from %d\n", fds[i].fd);
                char errors[100];
                std::memset(errors, '\0', 100);
                socklen_t length = sizeof(errors);
                if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &errors, &length) < 0) {
                    std::printf("get socket option failed\n");
                }
                continue;
            } else if (fds[i].revents & POLLRDHUP) {
                // 如果客户端关闭连接, 则服务器也关闭对应的连接, 并将用户总数减1
                clientData[fds[i].fd] = clientData[fds[userCounter].fd];
                close(fds[i].fd);
                fds[i] = fds[userCounter];
                --i;
                --userCounter;
                std::printf("a client left\n");
            } else if (fds[i].revents & POLLIN) {
                int connectFD = fds[i].fd;
                std::memset(clientData[connectFD].buffer, '\0', getBufferSize());
                ret = recv(connectFD, clientData[connectFD].buffer, getBufferSize() - 1, 0);
                std::printf("get %d bytes of client data %s from %d\n", ret, clientData[connectFD].buffer, connectFD);
                if (ret < 0) {
                    // 如果读操作出错, 则关闭连接
                    if (errno != EAGAIN) {
                        close(connectFD);
                        clientData[fds[i].fd] = clientData[fds[userCounter].fd];
                        fds[i] = fds[userCounter];
                        --i;
                        --userCounter;
                    }
                } else if (ret == 0) {

                } else {
                    for (int j = 0; j <= userCounter; ++j) {
                        if (fds[j].fd == connectFD) {
                            continue;
                        }
                        fds[j].events |= ~POLLIN;
                        fds[j].events |= POLLOUT;
                        clientData[fds[j].fd].writeBuffer = clientData[connectFD].buffer;
                    }
                }
            } else if (fds[i].revents & POLLOUT) {
                int connectFD = fds[i].fd;
                if (!clientData[connectFD].writeBuffer) {
                    continue;
                }
                ret = send(connectFD, clientData[connectFD].writeBuffer, std::strlen(clientData[connectFD].writeBuffer),
                           0);
                clientData[connectFD].writeBuffer = NULL;
                // 写完数据后需要重新注册fds[i]上的可读事件
                fds[i].events |= ~POLLOUT;
                fds[i].events |= POLLIN;
            }
        }
    }
    delete[] clientData;
    close(listenFD);

    return 0;
}


















////
//// Created by LiuYou on 2021/5/6.
////
//
//#define _GNU_SOURCE 1
//
//#include <cstdio>
//#include <cstring>
//#include <cstdlib>
//#include <netinet/in.h>
//#include <arpa/inet.h>
//#include <cassert>
//#include <sys/poll.h>
//#include <cerrno>
//#include <unistd.h>
//#include "ClientData.hpp"
//
//
//int main(int argc, char** argv) {
//    if (argc < 3) {
//        std::fprintf(stderr, "usage: %s IP Port\n", basename(argv[0]));
//        return -1;
//    }
//    const char* ip = argv[1];
//    int port = atoi(argv[2]);
//
//    sockaddr_in serverSockAddrIn{
//            .sin_family=PF_INET,
//            .sin_port=htons(port),
//            {.s_addr=0}
//    };
//    inet_pton(AF_INET, ip, &serverSockAddrIn.sin_addr);
//
//    int listenFD = socket(PF_INET, SOCK_STREAM, 0);
//    assert(listenFD >= 0);
//
//    int ret = bind(listenFD, (sockaddr * ) & serverSockAddrIn, sizeof(serverSockAddrIn));
//    assert(ret != -1);
//
//    ret = listen(listenFD, 5);
//    assert(ret != -1);
//
//    // 创建连接服务器的客户端(用户)的数组, 分配FD_LIMIT个ClientData对象。
//    // 这里的clientData取名为usersDate也是可以的。
//    int FD_LIMIT = 65535;
//    /// 我真的心态崩了, 这里我创建的不是数组！！！！, 啊啊啊啊啊啊~~~~~~~~~~~~~~~~~~。
//    /// 不过通过七八次调试终于反应过来了。
////    ClientData* clientUsersData = new ClientData[FD_LIMIT]();
//    ClientData* clientUsersData = new ClientData[FD_LIMIT];
//    // 限制连接到服务器的客户端(用户)的数量
//    int USER_LIMIT = 5;
//    // 这里加1的原因是将监听socket也加入到了poll进行监听。
//    pollfd pollFDS[USER_LIMIT + 1];
//    // TODO 注意: wCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC！！！！！！！！！！！
//    //  不知道是不是少了个等号的原因！！！！但是加上后测试还是不行！！！！ 算了, 吃饭去了。
////    for (int i = 1; i < USER_LIMIT; ++i) {
//    for (int i = 1; i <= USER_LIMIT; ++i) {
//        pollFDS[i].fd = -1;
//        pollFDS[i].events = 0;
//    }
//    pollFDS[0].fd = listenFD;
//    pollFDS[0].events = POLLIN | POLLERR;
//    pollFDS[0].revents = 0;
//
//    int userCount = 0;
//    for (;;) {
//        // 这里注意: userCount+1是因为:
//        // 刚开始是没有Client连接Server的, 但是poll是在监听(Server的监听socket)的！所以要加1。
//        ret = poll(pollFDS, userCount + 1, -1);
//        if (ret < 0) {
//            std::fprintf(stderr, "poll failure\n");
//            break;
//        }
//
//        // 这里和上面的注意是一个道理。
//        for (int i = 0; i < userCount + 1; ++i) {
//            // 此时Server启动, 在等待Client端的连接。
//            if ((pollFDS[i].fd == listenFD) && (pollFDS[i].revents & POLLIN)) {
//                sockaddr_in clientSockAddrIn{};
//                socklen_t clientSockAddrInLength = sizeof(clientSockAddrIn);
//
//                // 这里是我SB了。是我SB了。没有删掉的原因是警示自己要写代码时要看清！！！！
//                {
//                    // 注意这个C++新式的强转, 在这里是必要的。当然使用C式的强转也是可以的。下面的两个语句都是可以的。
//                    int connectionFD = accept(listenFD, (sockaddr * ) & clientSockAddrIn,
//                                              reinterpret_cast<socklen_t*>(&clientSockAddrIn));
////                int connectionFD = accept(listenFD, (sockaddr*) &clientSockAddrIn,
////                                          ((socklen_t*) &clientSockAddrIn));
//                }
//
//                int connectionFD = accept(listenFD, (sockaddr * ) & clientSockAddrIn, &clientSockAddrInLength);
//                if (connectionFD < 0) {
//                    // 这么写其实不是很好！我知道但是我不知道如何改进。
////                    std::fprintf(stderr, "errno is: %d\n", errno);
//                    std::printf("errno is: %d\n", errno);
//                    continue;
//                }
//
//                // 如果请求太多, 则关闭新到的连接。
//                if (userCount > USER_LIMIT) {
//                    const char* info = "too many clients(users)\n";
//                    std::fprintf(stderr, "%s", info);
//                    send(connectionFD, info, std::strlen(info), 0);
//                    close(connectionFD);
//                    continue;
//                }
//                // 对于新的Client连接, 同时修改pollFDS和clientData数组
//                ++userCount;
//                clientUsersData[connectionFD].setAddress(clientSockAddrIn);
//                setNonblocking(connectionFD);
//                pollFDS[userCount].fd = connectionFD;
//                pollFDS[userCount].events = POLLIN | POLLRDHUP | POLLERR;
//                pollFDS[userCount].revents = 0;
////                std::fprintf(stdout, "come a new user(client), now have %d users(clients)\n", userCount);
//                std::printf("comes a new user, now have %d users(clients)\n", userCount);
//            } else if (pollFDS[i].revents & POLLIN) {
//                // 这个是错误处理逻辑分支。我不懂。
//                std::fprintf(stdout, "get an error from %d\n", pollFDS[i].fd);
//                // ...
//                // 先略过
//            } else if (pollFDS[i].revents & POLLRDHUP) {
//                // 如果client关闭连接, 则Server也关闭对应的连接, 并将用户总数减1。
//                clientUsersData[pollFDS[i].fd] = clientUsersData[pollFDS[userCount].fd];
//                close(pollFDS[i].fd);
//                pollFDS[i] = pollFDS[userCount];
//                --i;
//                --userCount;
//                std::fprintf(stdout, "a client left\n");
//            } else if (pollFDS[i].revents & POLLIN) {
//                int connectFD = pollFDS[i].fd;
//                std::memset(clientUsersData[connectFD].buf, '\0', getBufferSize());
//                ret = recv(connectFD, clientUsersData[connectFD].buf, getBufferSize() - 1, 0);
//                std::printf("get %d bytes of client data %s from %d\n", ret, clientUsersData[connectFD].buf, connectFD);
//                if (ret < 0) {
//                    // 如果读操作出错, 则关闭连接
//                    if (errno != EAGAIN) {
//                        close(connectFD);
//                        clientUsersData[pollFDS[i].fd] = clientUsersData[pollFDS[userCount].fd];
//                        pollFDS[i] = pollFDS[userCount];
//                        --i;
//                        --userCount;
//                    }
//                } else if (ret == 0) {
//
//                } else {
//                    for (int j = 0; j <= userCount; ++j) {
//                        if (pollFDS[j].fd == connectFD) {
//                            continue;
//                        }
//                        pollFDS[j].events |= ~POLLIN;
//                        pollFDS[j].events |= POLLOUT;
//                        clientUsersData[pollFDS[j].fd].writeBuf = clientUsersData[connectFD].buf;
//                    }
//                }
//            } else if (pollFDS[i].revents & POLLOUT) {
//                int connectFD = pollFDS[i].fd;
//                if (!clientUsersData[connectFD].writeBuf) {
//                    continue;
//                }
//                ret = send(connectFD, clientUsersData[connectFD].writeBuf, std::strlen(clientUsersData[connectFD].writeBuf),
//                           0);
//                clientUsersData[connectFD].writeBuf = NULL;
//                // 写完数据后需要重新注册pollFDS[i]上的可读事件
//                pollFDS[i].events |= ~POLLOUT;
//                pollFDS[i].events |= POLLIN;
//            }
//
//        }
//    }
//    return 0;
//}